<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Map.Entry" %>
<%@ page import="triple.Triple" %>
<%@ page import="triple.node.BlankNode" %>
<%@ page import="triple.node.ImplicitPredicate" %>
<%@ page import="triple.node.Node" %>
<%@ page import="triple.node.TrunkNode" %>
<%@ page errorPage="index.jsp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type='text/javascript' src="javascript/xtree.js"></script>
<script type='text/javascript' src="javascript/util.js"></script>
<link type="text/css" rel="stylesheet" href="stylesheets/xtree.css">
<title>情报解析——三元组标注</title>
</head>
<style type='text/css'>
a {
	text-decoration: none;
}
[tag] {
	outline: red solid 2px;
}
table {
	background: black;
}
tr {
	background: white;
}
</style>
<%
	@SuppressWarnings("unchecked")
	List<Entry<String, String>> trunks = (List<Entry<String, String>>) session.getAttribute("trunks");

	@SuppressWarnings("unchecked")
	List<Triple> triples = (List<Triple>) request.getAttribute("triples");

	int maxBlankNodeId = Triple.getMaxBlankNodeId(triples);
%>
<body>
	<script type='text/javascript'>document.write(getTreeHTML(''));</script>
	<h2>以下是三元组标注结果，如果您不满意，可以手动修正，修正完毕请点“完成”</h2>
	<p id='intelligence'></p>
	<p id='triples'></p>
	<p id='BlankNodes'></p>
	
<script type="text/javascript">

// ------------- trunks ------------------ //
var trunks = [];
<%
	for (Entry<String, String> trunk : trunks)
		out.println("trunks.push(Trunk('" + trunk.getKey() + "','" + trunk.getValue() + "'));");
%>


function Trunk(content,type) {
	var trunk = new Object;
	trunk.content = content;
	trunk.type = type;
	trunk.toString = function() {
		var s = this.content + '/' + this.type;
		return s;
	};
	trunk.getClass = function(i) {
		return "trunk-" + i;
	};
	trunk.toHTML = function(i) {
		var html = "<a href='javascript: void(0)' " + 
					"onclick='setNode(this,event)' " + 
					"onmouseover='tagSameEntry(this)' " +
					"onmouseout='cancelTagSameEntry(this)' " +
					"id='" + this.getClass(i) + "' " +
					"class='" + this.getClass(i) + "' " +
					"title='" + this.type + "'>" + 
					"<strong>" + this.content + "</strong></a>";
		return html;
	};
	return trunk;
}

function trunks2HTML() {
	var html = '<p>';
	for (var i = 0; i < trunks.length; i ++) {
		html = html + trunks[i].toHTML(i) + " ";
	}
	return html + '</p>';
}

// -------------------- BlankNode ------------------ //
var blankNodes = [];
<%
	for (int i = 0; i <= maxBlankNodeId; i ++)
		out.println("BlankNode();");
%>

function BlankNode() {
	var node = new Object;
	node.id = blankNodes.length;
	node.toString = function() {
		return "BlankNode-" + this.id;
	};
	node.getClass = function() {
		return this.toString();
	};
	blankNodes.push(node);
	return node;
}


// ---------------- Node --------------- //
var BLANK_NODE = 0;
var IMPLICIT_PREDICATE = 1;
var TRUNK_NODE = 2;

var editingPointer;
var nodes = [];

function Node(type,entryIndex,alias) {
	var node = new Object;
	node.type = type;
	node.id = nodes.length;
	node.entryIndex = entryIndex;
	node.getEntry = function() {
		return (this.type == BLANK_NODE) ? blankNodes[this.entryIndex] : 
				(this.type == TRUNK_NODE) ? trunks[this.entryIndex] : '';
	};
	node.toString = function() {
		return this.getEntry().toString();
	};
	node.alias = (type == BLANK_NODE) ? node.toString() : 
					(type == IMPLICIT_PREDICATE) ? alias :
					alias ? alias : node.getEntry().content;
	node.getClass = function() {
		return (this.type == IMPLICIT_PREDICATE) ? 'implicit' : this.getEntry().getClass(this.entryIndex); 
	};
	node.getTitle = function() {
		var title = (this.type == BLANK_NODE) ? '空结点' : 
					(this.type == IMPLICIT_PREDICATE) ? '隐含词' : this.getEntry().type;
		return title;
	};
	node.toHTML = function() {
		var html = "<a href='javascript: void(0)' " + 
					"onclick='setNode(this,event)' " + 
					"ondblclick='editNode(this,event)' " +
					"onmouseover='tagSameEntry(this)' " + 
					"onmouseout='cancelTagSameEntry(this)' " + 
					"id='node-" + this.id + "' " +
					"class='" + this.getClass() + "' " + 
					"title='" + this.getTitle() + "'>" + 
					"<strong>" + (this.alias ? this.alias : '双击进行编辑') + "</strong></a>";
		return html;
	};
	node.toEditHTML = function() {
		var html;
		if ( this.type == BLANK_NODE ) {
			html = "<a href='javascript: void(0)' " +
					"onclick='doNothing(this,event)' " +
					"onmouseover='tagSameEntry(this)' " +
					"onmouseout='cancelTagSameEntry(this)' " +
					"id='node-" + this.id + "' " +
					"style='outline: yellow dotted 2px' " +
					"class='" + this.getClass() + "' " +
					"title='" + this.getTitle() + "''>" +
					"<strong>" + this.alias + "</strong></a>";
		} else {
			html = "<input id='node-" + this.id + "' " +
					"onkeypress='pressEnter(event)' " +
					"onmouseover='tagSameEntry(this)' " +
					"onmouseout='cancelTagSameEntry(this)' " + 
					"class='" + this.getClass() + "' " +
					"value='" + this.alias + "' " + 
					"onclick='doNothing(this,event)'/>";
		}
		return html;
	};
	nodes.push(node);
	return node;
}

function setNode(node,e) {
	if ( editingPointer ) {
		var type, alias;
		if ( node.id.indexOf('node-') == 0 ) {
			var currentNode = nodes[node.id.replace('node-','')];
			type = currentNode.type;
			alias = currentNode.alias;
			entryIndex = currentNode.entryIndex;
		} else {
			entryIndex = node.id.replace('trunk-','');
			type = TRUNK_NODE;
			alias = trunks[entryIndex].content;
		}
		var id = editingPointer.id;
		var editingNode = nodes[editingPointer.id.replace('node-','')];
		editingNode.type = type;
		editingNode.alias = alias;
		editingNode.entryIndex = entryIndex;
		
		editingPointer.parentElement.innerHTML = editingNode.toEditHTML();
		editingPointer = document.getElementById(id);
		if ( editingNode.type != BLANK_NODE )
			editingPointer.focus();
	}
	stopBubble(e);
}

function useNewBlankNode(e) {
	if ( editingPointer ) {
		var node = BlankNode();
		var editingNode = nodes[editingPointer.id.replace('node-','')];
		editingNode.type = BLANK_NODE;
		editingNode.alias = node.toString();
		editingNode.entryIndex = node.id;
		
		editingPointer.parentElement.innerHTML = editingNode.toHTML();
		editingPointer = '';
	} else {
		alert('请先指定需要编辑的结点');
	}
	reDraw();
	stopBubble(e);
}

function useNewImplicit(e) {
	if ( editingPointer ) {
		var id = editingPointer.id;
		var editingNode = nodes[editingPointer.id.replace('node-','')];
		editingNode.type = IMPLICIT_PREDICATE;
		editingNode.alias = '';
		
		editingPointer.parentElement.innerHTML = editingNode.toEditHTML();
		editingPointer = document.getElementById(id);
		editingPointer.focus();
	} else {
		alert('请先指定需要编辑的结点');
	}
	reDrawColor();
	stopBubble(e);
}

function pressEnter(event) {
	if ( event.keyCode == 13 )
		finishModifyNode();
}

function doNothing(obj,e) {
	stopBubble(e);
}

document.onclick = finishModifyNode;
function finishModifyNode() {
	if ( editingPointer ) {
		var index = editingPointer.id.replace('node-','');
		var className = editingPointer.className;
		if ( className.indexOf('BlankNode-') == 0 ) {
			editingPointer.parentElement.innerHTML = nodes[index].toHTML();
			editingPointer = '';
		} else {
			if ( editingPointer.value.length == 0 ) {
				alert('不能为空');
				editingPointer.value = nodes[index].alias;
				editingPointer.focus();
				return false;
			} else {
				nodes[index].alias = editingPointer.value;
				editingPointer.parentElement.innerHTML = nodes[index].toHTML();
				editingPointer = '';
			}
		}
		reDraw();
	}
	return true;
}

function editNode(node,e) {
	if ( !finishModifyNode() ) return;
	editingPointer = node;
	
	var className = editingPointer.className;
	if ( className.indexOf('BlankNode-') == 0 ) {
		editingPointer.setAttribute('style','outline: yellow dotted 2px');
	} else {
		var index = editingPointer.id.replace('node-','');
		var parent = editingPointer.parentElement;
		parent.innerHTML = nodes[index].toEditHTML();
		editingPointer = document.getElementById('node-' + index);
		editingPointer.focus();
	}
	stopBubble(e);
}

function cancelTagSameEntry(entry) {
	var className = entry.className;
	if ( className == 'implicit' ) {
		if ( entry != editingPointer ) {
//			entry.removeAttribute('style');
			entry.removeAttribute('tag');
		}
	} else {
		entities = document.getElementsByClassName(className);
		for (var i = 0; i < entities.length; i ++) {
			if ( entities[i] != editingPointer )
//				entities[i].removeAttribute('style');
				entities[i].removeAttribute('tag');
		}
	}
}

function tagSameEntry(entry) {
	u = entry;
	var className = entry.className;
	if ( className == 'implicit' ) {
		if ( entry != editingPointer )
//			entry.setAttribute('style','outline: red solid 2px');
			entry.setAttribute('tag','');
	} else {
		entities = document.getElementsByClassName(className);
		for (var i = 0; i < entities.length; i ++) {
			if ( entities[i] != editingPointer )
//				entities[i].setAttribute('style','outline: red solid 2px');
				entities[i].setAttribute('tag','');
		}
	}
}

var triples = [];
<%
	for (Triple triple : triples)
		out.println("triples.push(" + triple.toJSCreateString() + ");");
%>


function Triple(subject,predicate,object) {
	var triple = new Object;
	triple.subject = subject;
	triple.predicate = predicate;
	triple.object = object;
	triple.color = true;
	triple.toString = function() {
		return this.subject.toString() + ' ' + this.predicate.toString() + ' ' + this.object.toString();
	};
	triple.toHTML = function(i) {
		var html = "<input id='triple-color-" + i + "' " +
					"type='checkbox' " +
					(this.color ? 'checked' : '') + " " +
					"onclick='checkboxConvert(this)'>" +
					"<span id='triple-" + i + "'>" + 
					"<span>" + this.subject.toHTML() + "</span> " + 
					"<span>" + this.predicate.toHTML() + "</span> " + 
					"<span>" + this.object.toHTML() + "</span>";
		return html;
	};
	return triple;
}

function checkboxConvert(obj) {
	var index = obj.id.replace('triple-color-','');
	triples[index].color = !triples[index].color;
	reDrawColor();
}

function triples2HTML() {
	var html = "<table cellpadding='1px'><tr><td>";
	for (var i = 0; i < triples.length; i ++) {
		if ( !triples[i] ) continue;
		else if ( !triples[i].subject ) {
			html += "</td></tr><tr><td>";
			continue;
		}
		html = html + "<p>" + triples[i].toHTML(i) + " " +
				"<a id='remove-triple-" + i + "' " +
				"href='javascript: void(0)' " +
				"onclick='removeTriple(this)'>" +
				"remove</a></p>";
	}
	return html + "</td></tr></table>";
}

function removeTriple(triple) {
	var id = triple.id.replace('remove-triple-','');
	delete triples[id];
	reDraw();
}

function createTriple() {
	triples.push(Triple(
		Node(IMPLICIT_PREDICATE,'',''),
		Node(IMPLICIT_PREDICATE,'',''),
		Node(IMPLICIT_PREDICATE,'','')
	));
	reDraw();
}

function reDrawColor() {
	for (var i = 0; i < trunks.length; i ++) {
		var target = document.getElementById('trunk-' + i);
		target.removeAttribute('style');
	}
	for (var i = 0; i < triples.length; i ++) {
		if ( triples[i] && triples[i].color ) {
			if ( triples[i].subject.type == TRUNK_NODE ) {
				var index = triples[i].subject.entryIndex;
				var target = document.getElementById('trunk-' + index);
				target.setAttribute('style','background: yellow');
			}
			if ( triples[i].predicate.type == TRUNK_NODE ) {
				var index = triples[i].predicate.entryIndex;
				var target = document.getElementById('trunk-' + index);
				target.setAttribute('style','background: yellow');
			}
			if ( triples[i].object.type == TRUNK_NODE ) {
				var index = triples[i].object.entryIndex;
				var target = document.getElementById('trunk-' + index);
				target.setAttribute('style','background: yellow');
			}
		}
	}
}

function reDraw() {
	sortTriples();
	document.getElementById('intelligence').innerHTML = trunks2HTML();
	document.getElementById('triples').innerHTML = triples2HTML();
	reDrawColor();
	if ( editingPointer ) {
		var id = editingPointer.id;
		var editingNode = nodes[id.replace('node-','')];
		editingPointer = document.getElementById(id);
		editingPointer.parentElement.innerHTML = editingNode.toEditHTML();
		editingPointer = document.getElementById(id);
		editingPointer.focus();
	}
}

reDraw();


function checkValidate() {
	if ( triples ) {
		for (var i = 0; i < triples.length; i ++) {
			if ( triples[i] && triples[i].subject && (
					!triples[i].subject.alias ||
					!triples[i].predicate.alias ||
					!triples[i].object.alias )) {
				return false;
			}
		}
	}
	return true;
}

function sortTriples() {
	var newTriples = [];
	var visited = [];
	for (var i = 0; i < triples.length; i ++)
		visited[i] = false;
	
	var i = 0;
	while ( i < triples.length ) {
		if ( triples[i] && triples[i].subject && !visited[i] ) {
			var stack = [];
			newTriples.push(new Object);
			visited[i] = true;
			stack.push(triples[i]);
			while ( stack.length > 0 ) {
				var currentTriple = stack.pop();
				newTriples.push(currentTriple);
				var nears = fetchNearTripleIndexs(visited, currentTriple);
				// 稳定排序
				for (var j = nears.length - 1; j >= 0; j --) {
					visited[nears[j]] = true;
					stack.push(triples[nears[j]]);
				}
			}
		}
		i ++;
	}
	triples = newTriples;
	delete triples[0];
}

function fetchNearTripleIndexs(visited, triple) {
	var fetchedTripleIndexs = [];
	for (var i = 0; i < triples.length; i ++) {
		if ( triples[i] && triples[i].subject && !visited[i] ) {
			if ( tripleTripleIsNear(triples[i], triple) ) {
				fetchedTripleIndexs.push(i);
			}
		}
	}
	return fetchedTripleIndexs;
}

function nodeNodeIsNear(node1, node2) {
	return node1.type == BLANK_NODE && node2.type == BLANK_NODE && 
			node1.entryIndex == node2.entryIndex;
}

function nodeTripleIsNear(node, triple) {
	return nodeNodeIsNear(node, triple.subject) ||
			nodeNodeIsNear(node, triple.predicate) ||
			nodeNodeIsNear(node, triple.object);
}

function tripleTripleIsNear(triple1, triple2) {
	return nodeTripleIsNear(triple1.subject, triple2) ||
			nodeTripleIsNear(triple1.predicate, triple2) ||
			nodeTripleIsNear(triple1.object, triple2);
}

function submitTriples() {
	if ( !checkValidate() ) {
		alert('请把三元组补充完整');
		return false;
	}
	
	var postForm = document.createElement('form');
	postForm.method = 'post';
	postForm.action = 'Finish';
	var input;
	var count = 0;
	
	for (var i = 0; i < triples.length; i ++) {
		if ( triples[i] && triples[i].subject ) {
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-subject-type');
			input.setAttribute('value', triples[i].subject.type);
			postForm.appendChild(input);
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-subject-entryIndex');
			input.setAttribute('value', triples[i].subject.entryIndex);
			postForm.appendChild(input);
				
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-subject-alias');
			input.setAttribute('value', triples[i].subject.alias);
			postForm.appendChild(input);
				
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-predicate-type');
			input.setAttribute('value', triples[i].predicate.type);
			postForm.appendChild(input);
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-predicate-entryIndex');
			input.setAttribute('value', triples[i].predicate.entryIndex);
			postForm.appendChild(input);
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-predicate-alias');
			input.setAttribute('value', triples[i].predicate.alias);
			postForm.appendChild(input);
			
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-object-type');
			input.setAttribute('value', triples[i].object.type);
			postForm.appendChild(input);
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-object-entryIndex');
			input.setAttribute('value', triples[i].object.entryIndex);
			postForm.appendChild(input);
			
			input = document.createElement('input');
			input.setAttribute('name', 'triple-' + count + '-object-alias');
			input.setAttribute('value', triples[i].object.alias);
			postForm.appendChild(input);

			count ++;
		}
	}
	
	input = document.createElement('input');
	input.setAttribute('name', 'numOfTriples');
	input.setAttribute('value', count);
	postForm.appendChild(input);
	
	postForm.submit();
}

</script>
	<p>
	<button onclick='useNewBlankNode(event)'>使用一个新的BlankNode</button>
	<button onclick='useNewImplicit(event)'>使用一个新的隐含词</button>
	</p><p>
	<button onclick='createTriple()'>创建一个新的三元组</button>
	</p>
	
	<form action='AnnotateTriple' method='post'>
		<a href='index3.jsp'><button type='button'>上一步</button></a>
		<input type='button' onclick='submitTriples()' value='完成'>
	</form>
</body>
</html>